发布＆订阅  
一对多

示例
点咖啡，点好之后坐等被叫

```js
// 主题对象 (被观察者)
class Subject {
  constructor() {
    this.observers = []; // 观察者列表
  }

  // 添加观察者
  addObserver(observer) {
    this.observers.push(observer);
  }

  // 删除观察者
  removeObserver(observer) {
    const index = this.observers.indexOf(observer);
    if (index !== -1) {
      this.observers.splice(index, 1);
    }
  }

  // 通知观察者
  notifyObservers() {
    this.observers.forEach((observer) => {
      observer.update();
    });
  }
}

// 具体主题对象
class Button extends Subject {
  constructor() {
    super();
    this.clicked = false; // 按钮的状态
  }

  // 按钮被点击时更新状态并通知观察者
  click() {
    this.clicked = true;
    this.notifyObservers();
  }
}

// 观察者对象
class Observer {
  constructor(name) {
    this.name = name;
  }

  // 观察者收到通知后执行操作
  update() {
    console.log(`${this.name} 收到通知，执行相应操作`);
  }
}

// 创建主题和观察者
const button = new Button();
const observer1 = new Observer("Observer 1");
const observer2 = new Observer("Observer 2");

// 注册观察者
button.addObserver(observer1);
button.addObserver(observer2);

// 模拟按钮点击事件
button.click();

//Observer 1 收到通知，执行相应操作
//Observer 2 收到通知，执行相应操作
```

## 场景

- 网页事件绑定
  ```js
  $("$btn").click(function () {
    alert(1);
  });
  $("$btn").click(function () {
    alert(2);
  });
  $("$btn").click(function () {
    alert(3);
  });
  ```
- Promise

  ```js
  function loadImg() {
    var promise = new Promise(function (resolve, reject) {
      var img = document.createElement("img");
      img.onload = function () {
        resolve(img);
      };
      img.onerror = function () {
        reject("图片加载失败");
      };
      img.src = src;
    });
    return promise;
  }

  var src = "xxxx.jpg";
  var result = loadImg(src);

  result
    .then(function (img) {
      console.log("xxx", img.width);
      return img;
    })
    .then((img) => {
      console.log("xxx", img.height);
    })
    .catch((err) => {
      console.log(err);
    });
  ```

- jQuery callbacks

  ```js
  var callbacks = $.Callbacks();
  callbacks.add(function (info) {
    console.log("fn1", info);
  });

  callbacks.add(function (info) {
    console.log("fn2", info);
  });

  callbacks.add(function (info) {
    console.log("fn3", info);
  });
  ```

- nodejs 自定义事件

## 设计原则验证

- 主题和观察者分离，不是主动触发而是被动监听，两者解耦
- 符合开放封闭原则
